package Code.Basic.World;

import java.awt.Point;

import org.jrabbit.base.core.types.Renderable;
import org.jrabbit.base.data.structures.UpdateList;
import org.jrabbit.base.graphics.image.Image;
import org.jrabbit.base.graphics.transforms.Color;
import org.jrabbit.base.input.MouseHandler;
import org.jrabbit.base.managers.window.WindowManager;
import org.jrabbit.standard.game.graphics.skins.image.ImageSkin;
import org.jrabbit.standard.game.main.world.Layer;
import org.jrabbit.standard.game.main.world.World;
import org.lwjgl.opengl.GL11;

import Code.Basic.AI.Pathfinding.OpenZone;
import Code.Basic.AI.Pathfinding.PathMap;
import Code.Basic.Entity.GenericCharacter;
import Code.Basic.Entity.GenericEffect;
import Code.Basic.Entity.PhysicsSprite;
import Code.Objects.Characters.Player.Player;

public class GameWorld extends World
{
	private PhysicsInstance physicsHandler;

	protected UpdateList<GenericCharacter> characters;
	protected UpdateList<GenericEffect> effects;
	protected UpdateList<PhysicsSprite> obstacles;

	/* This seems a better way of removing objects. */
	protected UpdateList<GenericCharacter> charactersToDestroy;
	protected UpdateList<GenericEffect> effectsToDestroy;
	protected UpdateList<PhysicsSprite> obstaclesToDestroy;

	protected Player player;

	protected PathMap pathMap;
	
	private TerrainBase terrain;

	public GameWorld()
	{
		physicsHandler = new PhysicsInstance();
		characters = new UpdateList<GenericCharacter>();
		charactersToDestroy = new UpdateList<GenericCharacter>();
		effects = new UpdateList<GenericEffect>();
		effectsToDestroy = new UpdateList<GenericEffect>();
		obstacles = new UpdateList<PhysicsSprite>();
		obstaclesToDestroy = new UpdateList<PhysicsSprite>();

		addLayer("Terrain");
		addLayer("Terrain Decor");
		addLayer("Auras");
		addLayer("Decor");
		addLayer("Effects A");
		addLayer("Small Enemies");
		addLayer("Lasers");
		addLayer("Player");
		addLayer("Debris");
		addLayer("Effects B");
		addLayer("Big Enemies");
		addLayer("Structures");

		player = null;

		pathMap = new PathMap();
		
		background.color().set(0.565f, 0.561f, 0.494f);
		terrain = new TerrainBase();
	}

	public void initialize()
	{
		MouseHandler.setCursor("Resources/Images/GUI/Game/Combat Cursor.png", new Point(13, 13));
	}

	public void definePathMap()
	{

	}

	public void addPlayer(Player p)
	{
		player = p;
		addCharacter(p, "Player");
	}

	public void removePlayer()
	{
		removeCharacter(player);
		player = null;
	}

	public Player getPlayer()
	{
		return player;
	}

	public void addCharacter(GenericCharacter gC, String layer)
	{
		characters.add(gC);
		gC.setWorld(this);
		gC.resetZone();
		physicsHandler.add(gC.getBody());
		addRenderable(gC, layer);
		addUpdateable(gC);
	}

	public void addCharacter(GenericCharacter gC, OpenZone zone, String layer)
	{
		characters.add(gC);
		gC.setWorld(this);
		gC.resetZone();
		physicsHandler.add(gC.getBody());
		addRenderable(gC, layer);
		addUpdateable(gC);
	}

	public void removeCharacter(GenericCharacter gC)
	{
		charactersToDestroy.add(gC);
	}

	public void addObstacle(PhysicsSprite p, String layer)
	{
		obstacles.add(p);
		physicsHandler.add(p.getBody());
		addRenderable(p, layer);
		addUpdateable(p);
	}

	public void removeObstacle(PhysicsSprite p)
	{
		obstaclesToDestroy.add(p);
	}

	public void addEffect(GenericEffect e, String layer)
	{
		effects.add(e);
		e.setWorld(this);
		addRenderable(e, layer);
		addUpdateable(e);
	}

	public void removeEffect(GenericEffect e)
	{
		effectsToDestroy.add(e);
	}

	public UpdateList<GenericCharacter> getCharacters()
	{
		return characters;
	}

	public void updateWorld(int delta)
	{
		// Solve for physics.
		physicsHandler.update(getUpdateAmount());
		
		// Update the geometry of all effects, and then check to see if they should affect the target.
		for(GenericEffect e:effects)
		{
			for(GenericCharacter gC:characters)
			{
				gC.handleCollision(e);
			}
			characters.release();
			
			for(PhysicsSprite p:obstacles)
			{
				if(e.intersects(p))
				{
					e.collideWithStructure(p);
				}
			}
			obstacles.release();
		}
		effects.release();

		cull();
	}

	protected void cull()
	{
		for(GenericCharacter gC:charactersToDestroy)
		{
			characters.remove(gC);
			if (gC.getWorld() != null)
			{
				gC.setWorld(null);
				player.addScore(gC.getScore());
			}	
			physicsHandler.remove(gC.getBody());
			removeRenderable(gC);
			removeUpdateable(gC);
		}
		charactersToDestroy.release();
		
		for(GenericEffect gE:effectsToDestroy)
		{
			effects.remove(gE);
			gE.setWorld(null);
			removeRenderable(gE);
			removeUpdateable(gE);
		}
		effectsToDestroy.release();
		
		for(PhysicsSprite p:obstaclesToDestroy)
		{
			obstacles.remove(p);
			physicsHandler.remove(p.getBody());
			removeRenderable(p);
			removeUpdateable(p);
		}
		obstaclesToDestroy.release();

		charactersToDestroy.clear();
		effectsToDestroy.clear();
		obstaclesToDestroy.clear();
	}

	public void render()
	{
		GL11.glLoadIdentity();
		drawBackground();
		terrain.render();
		camera.bind();
		
		drawShadows();
		
		if(player != null)
		{
			player.renderObjectives();
		}
		for(Layer layer:rendered)
		{
			for(Renderable r:layer)
			{
				r.render();
			}
			layer.release();
		}
		if(player != null)
		{
			player.renderGUI();
		}
		camera.release();
	}

	protected void drawShadows()
	{
		for(GenericCharacter gC:characters)
		{
			gC.drawShadow();
		}
		characters.release();
	}


	/********************************/
	/** start of path finding code **/
	/********************************/

	public void setPathMap(PathMap map)
	{
		pathMap = map;
	}

	public PathMap getPathMap()
	{
		return pathMap;
	}

	/******************************/
	/** end of path finding code **/
	/******************************/

	private class TerrainBase implements Renderable
	{
		private ImageSkin terrain;
		
		public TerrainBase()
		{
			terrain = new ImageSkin("Resources/Images/Terrain/Tile 1.png");
		}

		public void render()
		{
			float screenWidth = WindowManager.getDisplayWidth();
			float screenHeight = WindowManager.getDisplayHeight();
			
			Image image = terrain.object();
			image.bind();
			Color.WHITE.bind();

			float width = (float) (screenWidth / camera.zoom().getScaleX()) / image.getWidth();
			float height = (float) (screenHeight / camera.zoom().getScaleY()) / image.getHeight();
			float startX = (float) (camera.location().x() % image.getWidth()) / image.getWidth();
			float startY = (float) (camera.location().y() % image.getHeight()) / image.getHeight();

			GL11.glBegin(GL11.GL_QUADS);

			GL11.glTexCoord2f(startX, startY);
			GL11.glVertex2f(0, 0);

			GL11.glTexCoord2f(startX + width, startY);
			GL11.glVertex2f(screenWidth, 0);

			GL11.glTexCoord2f(startX + width, startY + height);
			GL11.glVertex2f(screenWidth, screenHeight);

			GL11.glTexCoord2f(startX, startY + height);
			GL11.glVertex2f(0, screenHeight);

			GL11.glEnd();
		}
	}
}
